import {$$loading, $$notice} from 'plain-ui'
import {Fragment} from 'vue'

import {delay} from "plain-utils/utils/delay";
import {deepcopy} from 'plain-utils/object/deepcopy';
import {shuffle} from "plain-utils/object/shuffle";
import {throttle} from "plain-utils/utils/throttle";
import {debounce} from "plain-utils/utils/debounce";
import {defer} from "plain-utils/utils/defer";

/**
 * 解析html字符串
 * @author  韦胜健
 * @date    2020/9/16 9:54
 */
export async function processHtmlString(template?: string, js?: string, css?: string) {
    const Component = !js ? {} : await processScriptString(js);
    !!template && (Component.template = template);
    return {
        script: Component,
        style: css,
    }

}

const Babel = (window as any).Babel

async function buildDependency(output: string) {
    let names: string[] = []
    const regexp = /require\(['"](.*)['"]\)/g
    let match = regexp.exec(output)
    while (!!match) {
        names.push(match[1])
        match = regexp.exec(output)
    }
    await delay()
    const loading = $$loading.bar()
    try {
        const dependencies = (await Promise.all(names.map(async name => {
            if (name === 'vue') return {
                name,
                module: await import('vue'),
            }
            if (name === 'plain-utils') {
                return {
                    name,
                    module: {
                        delay,
                        deepcopy,
                        shuffle,
                        throttle,
                        debounce,
                        defer,
                    }
                }
            }
            if (name === 'plain-ui') return {
                name,
                module: await import('plain-ui')
            }
            if (name === 'plain-ui-composition') return {
                name,
                module: await import('plain-ui-composition')
            }
            if (name === 'icon-search') return {
                name,
                module: await import('../../components/IconSearch')
            }

            if (name === 'data.json') return {
                name,
                module: (await import('../../../../doc/data/data.json')).default,
            }
            if (name === 'data-1.json') return {
                name,
                module: (await import('../../../../doc/data/data-1.json')).default,
            }
            if (name === 'data-2.json') return {
                name,
                module: (await import('../../../../doc/data/data-2.json')).default,
            }
            if (name === 'tree.data.json') return {
                name,
                module: (await import('../../../../doc/data/tree.data.json')).default,
            }
            if (name === 'address.json') return {
                name,
                module: (await import('../../../../doc/data/address.json')).default,
            }

            const msg = `没有安装依赖：${name}`
            $$notice.warn(msg)
            throw new Error(msg)
        }))).reduce((prev, {name, module}) => {
            prev[name] = module
            return prev
        }, {} as Record<string, any>)
        return (name: string) => {
            return dependencies[name]
        }
    } catch (e) {
        console.error(e)
        setTimeout(() => loading.fail(), 300)
        return () => {}
    } finally {
        setTimeout(() => loading.done(), 300)
    }
}

async function processScriptString(scriptString: string) {

    scriptString = scriptString.replace(/<>/g, '<Fragment>').replace(/<\/>/g, '</Fragment>')

    const output = Babel.transform(scriptString, {
            plugins: [(window as any).VueJsxNext2],
            presets: [
                'env',
            ],
        }
    ).code;
    // console.log(output)
    const require = await buildDependency(output)

    const fn = new Function('module,exports,require,Fragment', output)
    const module = {exports: {} as any,}
    try {
        fn.call(module, module, module.exports, require, Fragment)
    } catch (e) {
        console.error(e)
        return null
    }
    return module.exports.default
}
